"""
RFM 是典型的用户分层方法，是评估用户消费能力、衡量用户贡献价值的重要工具。
RFM 代表的是最近一次消费时间间隔（Recency）、消费频率（Frequency）和消费金额（Monetary）。
本案例将利用 Pandas 建立用户消费 RFM 模型，实现精细化运营。
    1. 得到数据
    2. 分组求 r
    3. 分组求 f
    4. 组合 r、f 的过程求 m 形成基础的 rfm
    5. 打分数
    6. 进行 0, 1 标签，低于平均值为 0，高于为 1
    7. 根据权重建立标签-权重： r-100 f-10 m-1
    8. 转换为可以理解的标签
"""
import pandas as pd
from faker import Faker
from matplotlib import pyplot as plt

plt.rcParams['font.sans-serif'] = ['SimHei']

f = Faker(locale="zh_cn")

# 以下构造了两万条用户消费记录，
# 有用户名、购买时间和金额，且交易时间在近一年内，交易金额在 10 ～ 100 元之间。
df = pd.DataFrame(
    data={
        "用户": [f.name() for _ in range(20000)],
        "购买日期": [f.date_between(start_date="-1y", end_date="today") for _ in range(20000)],
        "金额": [f.random_int(10, 100) for _ in range(20000)]
    }
)

# 数据类型转换
df = df.astype({"购买日期": "datetime64[ns]"})
# 数据类型
# print(df.dtypes)
print(df)

"""
首先来计算 R 值。
R 为最后一次购买时间距今的天数，
R 值越大代表用户越有可能处于沉睡状态，流失风险越大：

R 为购买间隔天数
"""
# 显式选择需要操作的列
r = df.groupby("用户")["购买日期"].apply(lambda x: pd.Timestamp("today") - x.max()).dt.days
print(r)

"""
接下来计算 F 值。
F 值是消费频率，消费频次越高代表用户黏性越强。
我们将同一天购买多次的情况算作一次。
算法也是先对用户分组，然后取购买日期的不重复数量：

f 为购买次数，一天多次算一次
"""
f = df.groupby("用户")["购买日期"].nunique().sort_values(ascending=False)
print(f)

"""
M 值是金额，我们这里计算用户每次购物的平均金额，即用户总金额 / 用户购买次数。
由于前面已经算出购买次数，因此我们在合并数据时再计算 M 值，
这里先计算出每个用户的总金额：

m 为平均每次的购买金额
"""
# df.groupby("用户")["金额"].sum()

# 将 RFM 数据合并。
# 由于之前在计算 R 值和 F 值后都是以用户名称为索引的，
# 因此直接用两个 Series 构造 DataFrame，同时算出 M 值：
rfm = pd.DataFrame(data={"r": r, "f": f}) \
    .assign(m=lambda x: df.groupby(["用户"])["金额"].sum() / x.f)
print(rfm)

"""
这样，每个用户的 RFM 值就计算出来了。
接着给 RFM 打分，为了方便演示，采用 3 分制，将 RFM 的值分为三个等级。
R 值使用 pd.qcut() 平均分为三段，
R 越大代表间隔时间越长，对间隔近的打 3 分，次之打 2 分，最远的打 1 分。
F 值和 M 值越大越好，因此我们用 pd.cut() 人工分段，分别打 1、2、3 分
"""
rfm = rfm.assign(r_s=lambda x: pd.qcut(x["r"], q=3, labels=[3, 2, 1])) \
    .assign(f_s=lambda x: pd.cut(x["f"], bins=[0, 2, 5, float("inf")], labels=[1, 2, 3], right=False)) \
    .assign(m_s=lambda x: pd.cut(x["m"], bins=[0, 30, 60, float("inf")], labels=[1, 2, 3], right=False))
print(rfm)

"""
这样，就给每个用户的 RFM 完成了打分。
接下来进行分值归一化，
把高于平均水平的归为 1，
低于平均水平的归为 0
"""
rfm = rfm.assign(r_e=lambda x: (x["r_s"].astype(int) > x["r_s"].astype(int).mean()) * 1) \
    .assign(f_e=lambda x: (x["f_s"].astype(int) > x["f_s"].astype(int).mean()) * 1) \
    .assign(m_e=lambda x: (x["m_s"].astype(int) > x["m_s"].astype(int).mean()) * 1)
print(rfm)

"""
最后将这些打分形成一个统一的标签。
在打分设计时我们给正向的方面打了高分，
再将分值的重要度 R、F、M 分别转化为数字，放在百位、十位和个位：
"""
rfm = rfm.assign(label=lambda x: x.r_e * 100 + x.f_e * 10 + x.m_e * 1)

# 最后可以用 map 方法给数据打上中文标签：
label_names = {
    111: '重要价值用户',
    110: '一般价值用户',
    101: '重要发展用户',
    100: '一般发展用户',
    11: '重要保持用户',
    10: '一般保持用户',
    1: '重要挽留用户',
    0: '一般挽留用户'
}

rfm = rfm.assign(label_name=lambda x: x["label"].map(label_names))
print(rfm)
# 这样，将用户按分值由高到低分为 9 类，运营人员可以根据不同的用户类型来制定不同的营销策略。

rfm.groupby("label_name").count().plot.bar()
plt.show()
"""
之后的精细化运营

对用户分类以后要做什么呢？那就是针对每类用户如何制定运营策略，这个具体公司业务不同，方法也不一样。这里举例说明前4类用户。

1）重要价值用户，RFM 三个值都很高，
    要提供 VIP 服务

2）重要发展用户，消费频率低，但是其他两个值很高，
    就要想办法提高他的消费频率

3）重要保持用户，最近消费距离现在时间较远，也就是 F 值低，但是消费频次和消费金额高。这种用户，是一段时间没来的忠实客户。
    应该主动和他保持联系，提高复购率

4）重要挽留客户，最近消费时间距离现在较远、消费频率低，但消费金额高。这种用户，即将流失，
    要主动联系用户，调查清楚哪里出了问题，并想办法挽回。
"""
